//
// Created by Alex on 2021/9/17.
//

#ifndef COMPILER_UTILS_H
#define COMPILER_UTILS_H

#include <utility>
#include <vector>
#include <string>
#include <set>
#include <tuple>
#include <algorithm>
#include <unordered_map>
using namespace std;


#define DIMENSION_NOT_GET (-2)
#define DIMENSION_ERROR (-1)

enum SymType {
    VALUESYM, FUNCSYM
};

enum RetType {
    INT, VOID
};

enum NonTermlType {
    COMPUNIT, DECL, FUNCDEF, MAINFUNCDEF, CONSTDECL, CONSTDEF, CONSTEXP,
    CONSTINITVAL, ADDEXP, MULEXP, UNARYEXP, PRIMARYEXP, FUNCRPARAMS, UNARYOP,
    EXP, LVAL, NUMBER, VARDECL, VARDEF, INITVAL, FUNCTYPE, FUNCFPARAMS,
    FUNCFPARAM, BLOCK, STMT, BLOCKITEM, ASSIGNSTMT, SELECTIVEEXP, IFSTMT,
    WHILESTMT, BREAKSTMT, CONTINUESTMT, RETURNSTMT, PRINTFSTMT,
    COND, LOREXP, LANDEXP, EQEXP, RELEXP
};

struct str_hash { size_t operator()(const string& str) const; };

struct preserved {
private:
    static std::set<std::string> lex_set;
public:
    preserved();
    static bool is_preserved(const std::string& v);
    static std::string to_token(const std::string& v);
};

/*
 * lexical part
 */

struct lexical_result;
struct lexical_parse;
struct lexical_error;
struct preserved;

struct lexical_result {
    bool success = true;
    std::vector<lexical_parse> parses;
    std::vector<lexical_error> errors;
    void print_to_file(char *output_filename);
};

struct lexical_parse {
    int line_number{};
    std::string text;
    std::string type_code;
    std::string output;
    lexical_parse(int line_number, std::string text, std::string type_code);
    lexical_parse();
    lexical_parse(lexical_parse const &o);
    void print(FILE *stream) const;
};

struct lexical_error { int line_number; };

/*
 * syntax part
 */

struct syntax_result;
struct nonterminal;
struct CompUnit;
struct Decl;
struct FuncDef;
struct MainFuncDef;
struct ConstDecl;
struct ConstDef;
struct ConstExp;
struct ConstInitVal;
struct AddExp;
struct MulExp;
struct UnaryExp;
struct PrimaryExp;
struct FuncRParams;
struct UnaryOp;
struct Exp;
struct LVal;
struct Number;
struct VarDecl;
struct VarDef;
struct InitVal;
struct FuncType;
struct FuncFParams;
struct FuncFParam;
struct Block;
struct Stmt;
struct BlockItem;
struct AssignStmt;
struct SelectiveExp;
struct IfStmt;
struct WhileStmt;
struct BreakStmt;
struct ContinueStmt;
struct ReturnStmt;
struct PrintfStmt;
struct Cond;
struct LOrExp;
struct LAndExp;
struct EqExp;
struct RelExp;
struct Error;

struct Scope;
struct Symtab;
struct Symbol;
struct ValueSym;
struct FuncSym;

// symtab


struct Scope {
public:
    Symtab* symtab = nullptr;
    Scope* parent = nullptr;
    explicit Scope(Scope *_parent);
    Symbol* lookup(const string& name, SymType type) const;
};

struct Symtab {
    unordered_map<string, Symbol*> var_tab;
    unordered_map<string, Symbol*> func_tab;
    Symtab();
    Symbol* lookup(const string& name, SymType type);
    bool enter(Symbol *sym);
};

struct Symbol {
public:
    string name;
    SymType type;
    nonterminal *def;
    Symbol(string _name, SymType _type, nonterminal *_def);
};

struct ValueSym: public Symbol {
    bool is_const = false;
    bool is_param = false;
    explicit ValueSym(ConstDef *def);
    explicit ValueSym(VarDef *def);
    explicit ValueSym(FuncFParam *def);
    int dimension;
};

struct FuncSym: public Symbol {
    RetType ret_type;
    explicit FuncSym(FuncDef *def);
};

struct syntax_result {
    bool success;
    CompUnit *comp_unit;
    std::vector<Error> *error;
    syntax_result(CompUnit *_comp_unit, std::vector<Error> *_error);
    void print(char* output_filename, char *error_filename);
    void print(FILE *stream);
};

struct nonterminal {
public:
    NonTermlType obj_type;
    virtual void print(FILE *const stream) = 0;
    explicit nonterminal(NonTermlType type);
};
struct BlockItem : public nonterminal {
    explicit BlockItem(NonTermlType type);
};

struct CompUnit : public nonterminal, public Scope {
    std::vector<Decl*> *decls = nullptr;
    std::vector<FuncDef*> *func_defs = nullptr;
    MainFuncDef *main_func_def = nullptr;
    void add_content(std::vector<Decl*> *_decls, std::vector<FuncDef*> *_func_defs, MainFuncDef *_main_func_def);
    CompUnit();
    void print(FILE *stream) override;
};

struct Decl :  public BlockItem {
    explicit Decl(NonTermlType type);
};

struct FuncDef : public nonterminal {
public:
    FuncType * func_type;
    lexical_parse *ident;
    lexical_parse *lp;
    FuncFParams * func_fparams = nullptr;
    lexical_parse *rp;
    Block *block = nullptr;
    FuncDef(FuncType *_func_type, lexical_parse *_ident, lexical_parse *_lp,
            FuncFParams *_func_fparams,lexical_parse *_rp);
    FuncDef(FuncType *_func_type, lexical_parse *_ident, lexical_parse *_lp, lexical_parse *_rp);
    void add_content(Block *_block);
    explicit FuncDef(FuncDef *main_func_def);
    void print(FILE *stream) override;
};

struct MainFuncDef : public FuncDef {
    lexical_parse *btype;
    MainFuncDef(FuncDef *main_func_def);
    void print(FILE *stream) override;
};

struct FuncFParams : public nonterminal {
    FuncFParam * func_fparam;
    std::vector<std::pair<lexical_parse*, FuncFParam*>*> * func_fparams;
    std::vector<FuncFParam*> * real_fparams;
    FuncFParams(FuncFParam *_func_fparam, std::vector<std::pair<lexical_parse*, FuncFParam*>*> * _func_fparams);
    void print(FILE *stream) override;
    int get_line() const;
};

struct FuncFParam : public nonterminal {
    lexical_parse *btype;
    lexical_parse *ident;
    lexical_parse *lb = nullptr;
    lexical_parse *rb = nullptr;
    std::vector<std::tuple<lexical_parse*, ConstExp*, lexical_parse*>*> *exps = nullptr;
    FuncFParam(lexical_parse *_btype, lexical_parse *_ident);
    FuncFParam(lexical_parse *_btype, lexical_parse *_ident, lexical_parse *_lb, lexical_parse* _rb,
               std::vector<std::tuple<lexical_parse*, ConstExp*, lexical_parse*>*> *_exps);
    void print(FILE *stream) override;
    int get_line() const;
    int dimension = DIMENSION_NOT_GET;
    int get_dimension();
};

struct FuncType : public nonterminal {
    lexical_parse *btype;
    explicit FuncType(lexical_parse *_btype);
    void print(FILE *stream) override;
};

struct Stmt : public BlockItem {
    AssignStmt *assign_stmt = nullptr;
    SelectiveExp *selective_exp = nullptr;
    Block *block = nullptr;
    IfStmt *if_stmt = nullptr;
    WhileStmt *while_stmt = nullptr;
    BreakStmt *break_stmt = nullptr;
    ContinueStmt *continue_stmt = nullptr;
    ReturnStmt *return_stmt = nullptr;
    PrintfStmt *printf_stmt = nullptr;
    Stmt();
    explicit Stmt(AssignStmt *_stmt);
    explicit Stmt(SelectiveExp *_stmt);
    explicit Stmt(Block *_stmt);
    explicit Stmt(IfStmt *_stmt);
    explicit Stmt(WhileStmt *_stmt);
    explicit Stmt(BreakStmt *_stmt);
    explicit Stmt(ContinueStmt *_stmt);
    explicit Stmt(ReturnStmt *_stmt);
    explicit Stmt(PrintfStmt *_stmt);
    void add_content(nonterminal *stmt);
    void print(FILE *stream) override;
};

struct AssignStmt: public nonterminal {
    LVal *lval;
    lexical_parse *assign;
    Exp *exp = nullptr;
    lexical_parse *getint = nullptr;
    lexical_parse *lp = nullptr;
    lexical_parse *rp = nullptr;
    lexical_parse *semicn = nullptr;
    AssignStmt(LVal *_lval, lexical_parse *_assign ,Exp* _exp, lexical_parse *_semicn);
    AssignStmt(LVal *_lval, lexical_parse *_assign, lexical_parse *_getint,
               lexical_parse *_lp, lexical_parse *_rp, lexical_parse *_semicn);
    void print(FILE *stream) override;
};

struct SelectiveExp : public nonterminal {
    Exp *exp = nullptr;
    lexical_parse *semicn = nullptr;
    explicit SelectiveExp(lexical_parse *_semicn);
    SelectiveExp(Exp *_exp, lexical_parse *_semicn);
    void print(FILE *const stream) override;
};

struct IfStmt : public nonterminal {
    lexical_parse *if_tk;
    lexical_parse *lp;
    Cond *cond;
    lexical_parse *rp;
    Stmt *if_true;
    lexical_parse *else_tk = nullptr;
    Stmt *if_else = nullptr;
    IfStmt(lexical_parse *_if_tk, lexical_parse *_lp, Cond* _cond, lexical_parse *_rp,
           Stmt* _if_true);
    IfStmt(lexical_parse *_if_tk, lexical_parse *_lp, Cond* _cond, lexical_parse *_rp,
           Stmt* _if_true, lexical_parse *_else_tk, Stmt* if_else);
    void print(FILE *const stream) override;
};

struct Block : public nonterminal, public Scope {
    lexical_parse *lb = nullptr;
    std::vector<BlockItem*> *block_items = nullptr;
    lexical_parse *rb = nullptr;
    explicit Block(Scope *parent);
    void add_content(lexical_parse *_lb, std::vector<BlockItem*> *_block_items ,lexical_parse *_rb);
    void print(FILE *stream) override;
};

struct WhileStmt : public nonterminal {
    lexical_parse *while_tk;
    lexical_parse *lp;
    Cond* cond;
    lexical_parse *rp;
    Stmt* stmt;
    WhileStmt(lexical_parse *_while_tk, lexical_parse *_lp, Cond* _cond, lexical_parse *_rp, Stmt* _stmt);
    void print(FILE *stream) override;
};

struct BreakStmt: public nonterminal {
    lexical_parse *break_tk;
    lexical_parse *semicn;
    Stmt *outer;
    BreakStmt(lexical_parse *_break_tk, lexical_parse *_semicn, Stmt *_outer);
    void print(FILE *stream) override;
};

struct ContinueStmt: public nonterminal {
    lexical_parse *continue_tk;
    lexical_parse *semicn;
    Stmt *outer;
    ContinueStmt(lexical_parse *_continue_tk, lexical_parse *_semicn, Stmt *_outer);
    void print(FILE *stream) override;
};

struct ReturnStmt : public nonterminal {
    lexical_parse *return_tk;
    Exp* exp = nullptr;
    lexical_parse *semicn;
    ReturnStmt(lexical_parse *_return_tk, lexical_parse *_semicn);
    ReturnStmt(lexical_parse *_return_tk, Exp* _exp, lexical_parse *_semicn);
    void print(FILE *stream) override;
};

struct PrintfStmt: public nonterminal {
    lexical_parse *printf_tk;
    lexical_parse *lp;
    lexical_parse *format_string;
    std::vector<std::pair<lexical_parse*, Exp*>*> *exps;
    lexical_parse *rp;
    lexical_parse *semicn;
    PrintfStmt(lexical_parse *_printf_tk, lexical_parse *_lp, lexical_parse *_format_string,
               std::vector<std::pair<lexical_parse*, Exp*>*> *_exps, lexical_parse *_rp, lexical_parse *_semicn);
    void print(FILE *stream) override;
};

struct ConstDecl : public Decl {
    lexical_parse *const_tk;
    lexical_parse *btype;
    ConstDef *const_def;
    std::vector<std::pair<lexical_parse*, ConstDef*>*> *const_defs;
    lexical_parse *semicn;
    std::vector<ConstDef*> *real_const_defs;
    ConstDecl(lexical_parse *_const_tk, lexical_parse *_btype, ConstDef *_const_def,
              std::vector<std::pair<lexical_parse*, ConstDef*>*> *_const_defs, lexical_parse *_semicn);
    void print(FILE *stream) override;
};

struct ConstDef : public nonterminal {
    lexical_parse *ident;
    std::vector<std::tuple<lexical_parse*, ConstExp*, lexical_parse*>*> *exps;
    lexical_parse *assign;
    ConstInitVal *const_init_val;
    ConstDef(lexical_parse *_ident, std::vector<std::tuple<lexical_parse*, ConstExp*, lexical_parse*>*> *_exps,
             lexical_parse *_assign, ConstInitVal *_const_init_val);
    void print(FILE *stream) override;
    int get_line() const;
};

struct ConstExp : public nonterminal {
    AddExp *add_exp = nullptr;
    explicit ConstExp(AddExp *_add_exp);
    void print(FILE *stream) override;
    int get_line() const;
};

struct ConstInitVal : public nonterminal{
    ConstExp *const_exp = nullptr;
    lexical_parse *lb = nullptr;
    ConstInitVal *const_init_val = nullptr;
    std::vector<std::pair<lexical_parse*, ConstInitVal*>*> *const_init_vals = nullptr;
    std::vector<ConstInitVal*> *real_init_vals = nullptr;
    lexical_parse *rb = nullptr;
    explicit ConstInitVal(ConstExp* _const_exp);
    ConstInitVal(lexical_parse *_lb, lexical_parse *_rb);
    ConstInitVal(lexical_parse *_lb, ConstInitVal* _const_init_val,
                 std::vector<std::pair<lexical_parse*, ConstInitVal*>*> *_const_init_vals,
                 lexical_parse* _rb);
    void print(FILE *stream) override;
    int get_line() const;
};

struct AddExp : public nonterminal {
    AddExp *add_exp = nullptr;
    lexical_parse *opr = nullptr;
    MulExp *mul_exp = nullptr;
    AddExp(AddExp *_add_exp, lexical_parse *_plus_minus, MulExp *_mul_exp);
    explicit AddExp(MulExp *_mul_exp);
    void print(FILE *stream) override;
    int get_line() const;
    int dimension = DIMENSION_NOT_GET;
    int get_dimension();
};

struct MulExp :public nonterminal {
    MulExp *mul_exp = nullptr;
    lexical_parse *opr = nullptr;
    UnaryExp *unary_exp = nullptr;
    MulExp(MulExp *_mul_exp, lexical_parse *_opr, UnaryExp *_unary_exp);
    explicit MulExp(UnaryExp *_unary_exp);
    void print(FILE *stream) override;
    int get_line() const;
    int dimension = DIMENSION_NOT_GET;
    int get_dimension();
};

struct UnaryExp : public nonterminal {
    PrimaryExp *primary_exp = nullptr;
    lexical_parse *ident = nullptr;
    FuncSym *func_sym = nullptr;
    lexical_parse *lp = nullptr;
    FuncRParams *func_rparams = nullptr;
    lexical_parse *rp = nullptr;
    UnaryOp *unary_op = nullptr;
    UnaryExp *unary_exp = nullptr;
    explicit UnaryExp(PrimaryExp *_primary_exp);
    UnaryExp(lexical_parse *_ident, lexical_parse *_lp, lexical_parse *_rp, FuncSym * _func_sym);
    UnaryExp(lexical_parse *_ident, lexical_parse *_lp, FuncRParams *_func_rparams, lexical_parse *_rp, FuncSym *_func_sym);
    UnaryExp(UnaryOp *_unary_op, UnaryExp *_unary_exp);
    void print(FILE *stream) override;
    int get_line() const;
    int dimension = DIMENSION_NOT_GET;
    int get_dimension();
};

struct UnaryOp : public nonterminal {
    lexical_parse *opr = nullptr;
    explicit UnaryOp(lexical_parse *_opr);
    void print(FILE *stream) override;
};

struct PrimaryExp : public nonterminal {
    lexical_parse *lp = nullptr;
    Exp *exp = nullptr;
    lexical_parse *rp = nullptr;
    LVal *lval = nullptr;
    Number *number = nullptr;
    PrimaryExp(lexical_parse *_lp, Exp *_exp, lexical_parse *_rp);
    explicit PrimaryExp(LVal *_lval);
    explicit PrimaryExp(Number *_number);
    void print(FILE *stream) override;
    int get_line() const;
    int dimension = DIMENSION_NOT_GET;
    int get_dimension();
};

struct LVal: public nonterminal {
    lexical_parse *ident = nullptr;
    ValueSym *value_sym = nullptr;
    std::vector<std::tuple<lexical_parse*, Exp*, lexical_parse*>*> * exps = nullptr;
    LVal(lexical_parse *_ident, std::vector<std::tuple<lexical_parse*, Exp*, lexical_parse*>*> * _exps, ValueSym *_value_sym);
    void print(FILE *stream) override;
    int get_line() const;
    int dimension = DIMENSION_NOT_GET;
    int get_dimension();
};

struct Number : public nonterminal {
    lexical_parse *int_const = nullptr;
    explicit Number(lexical_parse *_int_const);
    void print(FILE *stream) override;
    int get_line() const;
    static int get_dimension() ;
};

struct FuncRParams : public nonterminal {
    Exp *exp;
    std::vector<std::pair<lexical_parse*, Exp*>*> *exps;
    std::vector<Exp*> *real_exps;
    FuncRParams(Exp *_exp, std::vector<std::pair<lexical_parse*, Exp*>*> *_exps);
    void print(FILE *stream) override;
    int get_line() const;
};


struct Exp :public nonterminal {
    AddExp *add_exp;
    explicit Exp(AddExp *_add_exp);
    void print(FILE *stream) override;
    int get_line() const;
    int get_dimension();
    int dimension = DIMENSION_NOT_GET;
};

struct VarDecl : public Decl {
    lexical_parse *btype;
    VarDef *var_def;
    std::vector<std::pair<lexical_parse*, VarDef*>*> *var_defs;
    lexical_parse *semicn;
    std::vector<VarDef*> *real_var_defs;
    VarDecl(lexical_parse *_btype, VarDef *_var_def,
            std::vector<std::pair<lexical_parse*, VarDef*>*> *_var_defs, lexical_parse *_semicn);
    void print(FILE *stream) override;
};

struct VarDef : public nonterminal {
    lexical_parse *ident;
    std::vector<std::tuple<lexical_parse*, ConstExp*, lexical_parse*>*> *exps;
    lexical_parse *assign = nullptr;
    InitVal *init_val = nullptr;
    VarDef(lexical_parse *_ident, std::vector<std::tuple<lexical_parse*, ConstExp*, lexical_parse*>*> *_exps);
    VarDef(lexical_parse *_ident, std::vector<std::tuple<lexical_parse*, ConstExp*, lexical_parse*>*> *_exps,
           lexical_parse *_assign, InitVal *_init_val);
    void print(FILE *stream) override;
    int get_line() const;
};

struct InitVal : public nonterminal {
    Exp *exp = nullptr;
    lexical_parse *lb = nullptr;
    InitVal *init_val = nullptr;
    std::vector<std::pair<lexical_parse*, InitVal*>*> *init_vals = nullptr;
    lexical_parse *rb = nullptr;
    explicit InitVal(Exp* _exp);
    InitVal(lexical_parse* _lb, lexical_parse *_rb);
    InitVal(lexical_parse* _lb, InitVal * _init_val,
            std::vector<std::pair<lexical_parse*, InitVal*>*> *_init_vals, lexical_parse *_rb);
    void print(FILE *stream) override;
    int get_line() const;
};

struct Cond: public nonterminal {
    LOrExp *lor_exp;
    explicit Cond(LOrExp * _lor_exp);
    void print(FILE *stream) override;
    int get_line() const;
};

struct LOrExp : public nonterminal {
    LOrExp *lor_exp = nullptr;
    lexical_parse *or_op = nullptr;
    LAndExp *land_exp;
    explicit LOrExp(LAndExp *_land_exp);
    LOrExp(LOrExp *_lor_exp, lexical_parse *_or_op, LAndExp *_land_exp);
    void print(FILE *stream) override;
    int get_line() const;
};

struct LAndExp : public nonterminal {
    LAndExp *land_exp = nullptr;
    lexical_parse *and_op = nullptr;
    EqExp *eq_exp = nullptr;
    explicit LAndExp(EqExp* _eq_exp);
    LAndExp(LAndExp *_land_exp, lexical_parse *_and_op, EqExp *_eq_exp);
    void print(FILE *stream) override;
    int get_line() const;
};

struct EqExp : public  nonterminal {
    EqExp * eq_exp = nullptr;
    lexical_parse *opr = nullptr;
    RelExp* rel_exp = nullptr;
    explicit EqExp(RelExp* _rel_exp);
    EqExp(EqExp* _eq_exp, lexical_parse *_opr, RelExp *_rel_exp);
    void print(FILE *stream) override;
    int get_line() const;
};

struct RelExp: public nonterminal {
    RelExp* rel_exp = nullptr;
    lexical_parse *opr = nullptr;
    AddExp *add_exp;
    explicit RelExp(AddExp* _add_exp);
    RelExp(RelExp* _rel_exp, lexical_parse *_opr, AddExp *_add_exp);
    void print(FILE *stream) override;
    int get_line() const;
};

struct Error {
    int line;
    char type;
    Error(int _line, char _type);
    void print(FILE *stream) const;
    bool operator<(const Error & o) const;
};


#endif //COMPILER_UTILS_H
